package ru.greeneyes.socksThrough;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ivanalx
 * @date 20.05.2009 14:57:24
 */
public class ProxyThrower implements Runnable {
	private SocksProperties properties;
	private static final int BUFFER_SIZE = 10240;

	public ProxyThrower(SocksProperties properties) {
		this.properties = properties;
	}

	public void run() {
		try {
			ServerSocket serverSocket = new ServerSocket();
			InetSocketAddress bindAddress;
			if (properties.getLocalHost() == null || "".equals(properties.getLocalHost().trim())) {
				bindAddress = new InetSocketAddress(properties.getLocalPort());
			} else {
				bindAddress = new InetSocketAddress(properties.getLocalHost(), properties.getLocalPort());
			}

			Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(properties.getSocksProxyHost(), properties.getSocksProxyPort()));
			serverSocket.bind(bindAddress);
			while (!Thread.currentThread().isInterrupted()) {
				final Socket client = serverSocket.accept();
				try {
					client.setSoTimeout(1000);
					final Socket socks = new Socket(proxy);
					InetSocketAddress endPoint = new InetSocketAddress(properties.getTargetHost(), properties.getTargetPort());
					socks.setSoTimeout(1000);
					socks.connect(endPoint, 1000);

					System.out.println("Connection from " + client + " with proxy " + proxy + " to real address: " + endPoint + " is OK");
					final List<ReaderThread> readers = new ArrayList<ReaderThread>();

					Runnable closeAll = new Runnable() {
						public void run() {
							try {
								socks.close();
							} catch (Exception e) {
								//forget
							}
							try {
								client.close();
							} catch (Exception e) {
								//forget
							}
							for (ReaderThread reader : readers) {
								try {
									reader.stop();
								} catch (Exception e) {
									//forget
								}
							}
						}
					};

					readers.add(new ReaderThread(socks.getInputStream(), "SERVICE" , client.getOutputStream(), "CLIENT", closeAll, BUFFER_SIZE));
					readers.add(new ReaderThread(client.getInputStream(), "CLIENT", socks.getOutputStream(), "SERVICE" , closeAll, BUFFER_SIZE));

					for (ReaderThread reader : readers) {
						Thread d = new Thread(reader, "Readers");
						d.setDaemon(true);
						d.start();
					}
				} catch (Exception e) {
					System.err.println("Fail to connect with remote target: " + e);
				}
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
}
